#include <stdio.h>
#include <assert.h>
#include "cache.h"

#include "bus.h"




#define LINE(a) (((a)>>nBytesInBits)&LineIdxMask())
#define TAG(a) ((a)>>(nBytesInBits+nLinesInBits))
#define LOFFSET(a) ((a)&((1<<nBytesInBits)-1))

template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool  CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
read_byte (__u32 address,__u8 *p_dat)
{
	unsigned int n=LINE(address);
	unsigned int i;
	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address))
		{
			*p_dat=lines[n][i].data[LOFFSET(address)];
			ages[n]=lru_ctrl.NewAge(ages[n],i);
			return true; //hit
		}

	}
	
	
	return false;//miss
	
}
//
// assume, the word not across two lines.
//
template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 read_short(__u32 address,__u16 *p_dat)
{
	unsigned int n=LINE(address);
	unsigned int i;
	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address))
		{
			assert(LOFFSET(address)<nBytes()-1);
			*p_dat=lines[n][i].data[LOFFSET(address)]|
			(lines[n][i].data[LOFFSET(address)+1]<<8);

			ages[n]=lru_ctrl.NewAge(ages[n],i);
			return true; //hit
		}

	}
	
	
	return false;//miss
	
}
//
// assume, the dword not across two lines.
//
template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 read_int(__u32 address,__u32 *p_dat)
{
	unsigned int n=LINE(address);
	unsigned int i;
	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address))
		{
			// within a line  

			assert(LOFFSET(address)<nBytes()-3);
			
			*p_dat=lines[n][i].data[LOFFSET(address)]|
			(lines[n][i].data[LOFFSET(address)+1]<<8)|
			(lines[n][i].data[LOFFSET(address)+2]<<16)|
			(lines[n][i].data[LOFFSET(address)+3]<<24);

			ages[n]=lru_ctrl.NewAge(ages[n],i);
			return true; //hit
			

		}

	}
	
	
	return false;//miss
}

template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 write_byte (__u32 address,__u8  dat,__u8 *p_line_out)
{
	unsigned int n=LINE(address);
	unsigned int i;
	bool swapout=false;

	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address)) break;
	}

	// if not hit
	if(i==nWays())
	{
		// if the oldest way is dirty, swap it out
		i=lru_ctrl.Oldest(ages[n]);
		if(lines[n][i].state==Modified)
		{
			swapout=true;
		}
		memcpy(p_line_out,lines[n][i].data,(1<<nBytesInBits));
	
	}
	// write
	lines[n][i].data[LOFFSET(address)]=(__u8)(dat);

	// update age,state
	ages[n]=lru_ctrl.NewAge(ages[n],i);

	lines[n][i].state=Modified;
	return swapout;
}
//
// assume, the word not across two lines.
//
template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 write_short(__u32 address,__u16 dat,__u8 *p_line_out)
{
	unsigned int n=LINE(address);
	unsigned int i;
	bool swapout=false;

	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address)) break;
	}

	// if not hit
	if(i==nWays())
	{
		// if the oldest way is dirty, swap it out
		i=lru_ctrl.Oldest(ages[n]);
		if(lines[n][i].state==Modified)
		{
			swapout=true;
		}
		memcpy(p_line_out,lines[n][i].data,(1<<nBytesInBits));
	
	}
	// write
	lines[n][i].data[LOFFSET(address)]=(__u8)(dat);
	lines[n][i].data[LOFFSET(address)+1]=(__u8)(dat>>8);

	// update age,state
	ages[n]=lru_ctrl.NewAge(ages[n],i);

	lines[n][i].state=Modified;
	return swapout;
}
//
// assume, the dword not across two lines.
//
template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 write_int(__u32 address,__u32 dat,__u8 *p_line_out)
{
	unsigned int n=LINE(address);
	unsigned int i;
	bool swapout=false;

	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address)) break;
	}

	// if not hit
	if(i==nWays())
	{
		// if the oldest way is dirty, swap it out
		i=lru_ctrl.Oldest(ages[n]);
		if(lines[n][i].state==Modified)
		{
			swapout=true;
		}
		memcpy(p_line_out,lines[n][i].data,(1<<nBytesInBits));
	
	}
	// write
	lines[n][i].data[LOFFSET(address)]=(__u8)(dat);
	lines[n][i].data[LOFFSET(address)+1]=(__u8)(dat>>8);
	lines[n][i].data[LOFFSET(address)+2]=(__u8)(dat>>16);
	lines[n][i].data[LOFFSET(address)+3]=(__u8)(dat>>24);
	// update age,state
	ages[n]=lru_ctrl.NewAge(ages[n],i);

	lines[n][i].state=Modified;
	return swapout;
		

}

template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 read_line(__u32 address,__u8 * p_data)
{
	unsigned int n=LINE(address);
	unsigned int i;

	assert(LOFFSET(address)==0);

	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address))
		{
			// within a line  
				
			memcpy(p_data,lines[n][i].data,(1<<nBytesInBits));

			ages[n]=lru_ctrl.NewAge(ages[n],i);
			return true; //hit
			

		}
	}
	

	return false;//miss
}

template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 fill_line(__u32 address,__u8 * p_data,__u8 *p_line_out)
{
	unsigned int n=LINE(address);
	unsigned int i;
	bool swapout=false;

	assert(LOFFSET(address)==0);

	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address)) break;
	}

	// if not hit
	if(i==nWays())
	{
		// if the oldest way is dirty, swap it out
		i=lru_ctrl.Oldest(ages[n]);
		if(lines[n][i].state==Modified)
		{
			swapout=true;
		}
		memcpy(p_line_out,lines[n][i].data,(1<<nBytesInBits));
	
	}
	// write
	memcpy(lines[n][i].data,p_data,(1<<nBytesInBits));

	// update age,state
	ages[n]=lru_ctrl.NewAge(ages[n],i);

	lines[n][i].state=Valid;
	return swapout;
}


template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
bool CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 write_line(__u32 address,__u8 * p_data,__u8 *p_line_out)
{
	unsigned int n=LINE(address);
	unsigned int i;
	bool swapout=false;

	assert(LOFFSET(address)==0);

	for(i=0;i<nWays();i++)
	{
		if(lines[n][i].tag==TAG(address)) break;
	}

	// if not hit
	if(i==nWays())
	{
		// if the oldest way is dirty, swap it out
		i=lru_ctrl.Oldest(ages[n]);
		if(lines[n][i].state==Modified)
		{
			swapout=true;
		}
		memcpy(p_line_out,lines[n][i].data,(1<<nBytesInBits));
	
	}
	// write
	memcpy(lines[n][i].data,p_data,(1<<nBytesInBits));

	// update age,state
	ages[n]=lru_ctrl.NewAge(ages[n],i);

	lines[n][i].state=Modified;
	return swapout;
}


template <int nLinesInBits, int nWaysInBits,int nBytesInBits,class T>
void CCache<nLinesInBits, nWaysInBits,nBytesInBits, T>::
	 invalidate_all()
{
	unsigned int i,j;

	for(i=0;i<(1<<nLinesInBits);i++)
	{
		for(j=0;j<(1<<nWaysInBits);j++)
			lines[i][j].state=Invalid;
	}
	ages[i]=0;
}



template<int n,class T>
bool  CPsuedoLRUCtrl<n,T>::ok;

template<int n,class T>
T CPsuedoLRUCtrl<n,T>::one_mask[n];

template<int n,class T>
T CPsuedoLRUCtrl<n,T>::zero_mask[n];

template<int n,class T>
CPsuedoLRUCtrl<n,T>::CPsuedoLRUCtrl()
{
	if(!ok)
	{
		unsigned int bit=1;
		unsigned int step=n,i;

		//
		// gen one_mask/zero_mask table
		//
		do
		{
			step>>=1;
			for(i=0;i<n;)
			{
				unsigned j;
				for(j=0;j<step;j++,i++)
				{
					one_mask[i]|=bit;
				}
				for(j=0;j<step;j++,i++)
				{
					zero_mask[i]|=bit;
				}

		
			}
			bit<<=1;
		}while(step>1);

		//printf("\n");
		//for(i=0;i<n;i++)
		//{
		//	printf("%02x ",one_mask[i]);
		//}
		//printf("\n");
		//for(i=0;i<n;i++)
		//{
		//	printf("%02x ",zero_mask[i]);
		//}
	
		ok=true;

	}
}
template<int n,class T>
int CPsuedoLRUCtrl<n,T>::Oldest(T age)
{
	unsigned int i;
	//
	//  by one_mask=0 && by zero_mask =1
	//
	for(i=0;i<n;i++)
	{
		if ((age&one_mask[i])==0
			&&(age&zero_mask[i])==zero_mask[i])

		return i;
	}
	//
	// some ways not filled.find the next free way
	//
	for(i=0;i<n;i++)
	{
		if ((age&one_mask[i])==0
			&&(age&zero_mask[i])==zero_mask[i])
		return i;
	}

	assert(0);
	return 0;
}
template<int n,class T>
T CPsuedoLRUCtrl<n,T>::NewAge(T age, T youngest)
{

	age|=one_mask[youngest];
	age &=~zero_mask[youngest];
	return age;
}